package com.extract.query;

import com.extract.exception.DatabaseException;
import com.extract.sys.entity.DatabaseSource;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author: chenfenghai
 * @version: 1.0
 * @date: 2020-10-15 13:36
 */
@Slf4j
@Component
public class DatabaseBuilder {

    @Autowired
    HikariConfig hikariConfig;

    @Autowired
    SqlSessionFactory sqlSessionFactory;

    private static final String KEY_FORMAT_STRING = "_";
    private static final String POOL_NAME_PREFIX = "pool_";
    private static final Map<String, HikariDataSource> HIKARI_DATA_SOURCE_MAP
            = Maps.newConcurrentMap();

    /**
     * 添加数据源
     *
     * @param databaseSource 数据库资源表记录
     */
    public void addDataSource(DatabaseSource databaseSource) {
        try {
            String key = getDataSourceKey(databaseSource.getTenantId(),
                    databaseSource.getCinemaCode(), databaseSource.getSystemCode());
            HikariConfig config = new HikariConfig();
            hikariConfig.copyStateTo(config);
            config.setPoolName(StringUtils.join(POOL_NAME_PREFIX, key));
            config.setJdbcUrl(StringUtils.trimToEmpty(databaseSource.getDbJdbcUrl()));
            config.setUsername(StringUtils.trimToEmpty(databaseSource.getDbUsername()));
            config.setPassword(StringUtils.trimToEmpty(databaseSource.getDbPassword()));
            HIKARI_DATA_SOURCE_MAP.putIfAbsent(key, new HikariDataSource(config));
        }catch (Exception e) {
            log.error("数据库连接异常", e);
        }
    }

    /**
     * 获取数据库连接
     *
     * @param database 数据库实体
     * @return Connection
     */
    public Connection getConnection(SourceUnique database) {
        Objects.requireNonNull(database);
        return getConnection(
                database.getTenantId(),
                database.getCinemaCode(),
                database.getSystemCode());
    }

    /**
     * 获取数据库连接
     *
     * @param tenantId   租户ID
     * @param cinemaCode 影院编码
     * @param systemCode 系统编码
     * @return Connection
     */
    public Connection getConnection(final String tenantId,
                                    final String cinemaCode,
                                    final String systemCode) {
        String key = getDataSourceKey(tenantId, cinemaCode, systemCode);
        HikariDataSource dataSource = HIKARI_DATA_SOURCE_MAP.get(key);
        if (Objects.isNull(dataSource)) {
            throw new DatabaseException("HikariDataSource is null");
        }
        try {
            return dataSource.getConnection();
        } catch (Exception e) {
            log.error("获取数据库连接失败", e);
            throw new DatabaseException("get connection throw exception", e);
        }
    }

    /**
     * 根据ID及参数获取SQL
     *
     * @param id              SQL id
     * @param parameterObject 参数
     * @return String
     */
    public String getSql(String id, Object parameterObject) {
        return sqlSessionFactory.getConfiguration()
                .getMappedStatement(id).getBoundSql(parameterObject)
                .getSql();
    }

    /**
     * 查询数据
     *
     * @param extractQuery 查询条件
     * @return List
     */
    public List<Map<String, String>> queryData(ExtractQuery extractQuery) {
        Objects.requireNonNull(extractQuery);
        List<Map<String, String>> objectList = Lists.newArrayList();
        String sql = getSql(extractQuery.getId(), extractQuery.getParameterObject());
        log.info("query sql: \n{}", sql);
        try (
                Connection conn = getConnection(extractQuery);
                PreparedStatement statement = conn.prepareStatement(sql);
                ResultSet rs = statement.executeQuery()
        ) {
            int col = rs.getMetaData().getColumnCount();
            while (rs.next()) {
                Map<String, String> resultMap = Maps.newHashMap();
                for (int i = 1; i <= col; i++) {
                    resultMap.put(rs.getMetaData().getColumnLabel(i), rs.getString(i));
                }
                objectList.add(resultMap);
            }
        } catch (Exception e) {
            throw new DatabaseException("query data throw exception", e);
        }
        return objectList;
    }

    /**
     * 获取数据源KEY
     *
     * @param tenantId   租户ID
     * @param cinemaCode 影院编码
     * @param systemCode 系统编码
     * @return String
     */
    public String getDataSourceKey(final String tenantId,
                                   final String cinemaCode,
                                   final String systemCode) {
        return StringUtils.joinWith(KEY_FORMAT_STRING,
                StringUtils.trimToEmpty(tenantId).toUpperCase(),
                StringUtils.trimToEmpty(cinemaCode.toUpperCase()),
                StringUtils.trimToEmpty(systemCode).toUpperCase());
    }
}
